1
  • 参数

    • 形参(parameter):函数中定义的变量

    • 实参(argument):运行时的函数调用时传入的参数

  • 上下文(context):通过对象来调用函数时,这个对象就是this就是上下文;

  • 闭包(closure):函数嵌套在函数当中

1 函数调用的方式

有4种方法来调用JavaScript函数:

  • 作为函数

  • 作为方法

  • 作为构造函数

  • 通过call方法和apply方法间接调用

ES3 和非严格模式下的ES5中,调用上下文(this的值)是全局对象。在严格模式下,调用上下文则是undefined

1.1 函数调用

语法:

functionName()


window.name = "hello";
(function () {
    console.log(this); //window
})()

"use strict";
window.name = "hello";
(function () {
    console.log(this); //undefined
})()

1.2 方法调用

语法:

obj.functionName()

方法和this关键字是面向对象编程规范的核心

方法链

  • 方法链:当方法的返回值是一个对象,这个对象还可以再调用它的方法。这个方法调用序列就是方法链;

1.3 构造函数调用

没有形参的构造函数调用都可以省略小括号

1.4 间接调用

  • call方法:使用它自有的实参列表作为函数的实参;

  • apply方法:要求以数组的形式传入参数;

2 函数的实参和形参

2.1 可选形参

当调用函数的时候传入的实参比函数声明时指定的形参个数要少,剩下的形参都将是undefined;所以在定义函数的时候应当给省略的参数赋一个默认值:


function pushArray(o, /* optional */ a) {
    if (a === undefined) a = [];
    // a = a || [];
    for (var property in o) a.push(property);
    return a;
}
var o = {
    name: "Oliver",
    age: 18
};
var a = ["friends"];
console.log(pushArray(o, a).toString()); //friends,name,age

2.2 实参对象

省略的实参都是undefined;多出的参数自动忽略

另外,可以通过arguments来获取多余的参数,arguments.length获得参数个数

  • 不定实参函数(varargs function):接收任意个数的实参

不定实参函数的实参个数不能为零;

ES5之前,arguments[0]和x指代同一个值;

function pushArray(a) {   
    console.log(a); //Array
    arguments[0] = 10;
    console.log(a); //Array
    console.log(arguments[0]); //10
}
var a = ["friends"];
pushArray(a);

2.3 将对象属性用作实参

最好通过名值对的形式传入参数(传入的实参都写入一个单独的对象之中,在调用的时候传入一个对象)


function outputString(obj) {
    var fname = obj.firstName,
        lname = obj.lastName,
        age = obj.age;
    console.log(`your fisrtName: ${fname}; lastName: ${lname}; age: ${age}`);
}
outputString({firstName: "Oliver", lastName: "Young", age: 18}); //your fisrtName: Oliver; lastName: Young; 

3 作为值的函数

函数也是值,可以将函数赋值给变量,存储在对象的属性和数组的元素中,作为参数传入另外一个函数等

函数是对象,可以定义自己的属性

log.conter = 10;
function log () {
    console.log(log.conter);
}
log(); //10

这样函数就不需要再定义一个全局变量

4作为命名空间的函数

将代码放入一个函数内,然后调用这个函数:


不同的写法:

function log (i) {
    console.log("hello" +i);
}
log(100);

(function log (i) {
    console.log("hello" + i);
}(100))

(function log (i) {
    console.log("hello" + i);
})(100)

小括号括起来避免JavaScript解释器解析为函数声明语句

5 闭包

  • 词法作用域(lexical scoping):函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的。

  • 闭包:函数对象可以通过作用域相互关联起来,函数体内部的变量都可以保存在函数作用域内。

上面的log.conter属性就可以使用包括在闭包中:

var pro = (function() {
    var conter = 10;

    function log(argument) {
        console.log(conter++);
    }
    return log;
})();
pro(); //10
pro(); //11
pro(); //12

使用多个嵌套函数:

function counter () {
    var num = 0;
    return {
        count: function () {
            return num ++;
        },
        reset: function () {
            num = 0;
        }
    };
}
var newCounter = counter(); //这里是新的计数器,再赋值一个变量可以得到另外一个计数器,两者不受影响
console.log(newCounter.count()); //0
console.log(newCounter.count()); //1
console.log(newCounter.count()); //2
newCounter.reset();
console.log(newCounter.count()); //0
console.log(newCounter.count()); //1
console.log(newCounter.count()); //2

6 函数属性、方法和构造函数

6.1 length属性

函数的length返回的是形参的数量;函数内部arguments.length返回的是实参的数量

function add(x, y) {
    return arguments.length;
}
console.log(add.length); //2
console.log(add(1)); //1

6.2 prototype属性

6.3 call()和apply()方法

通过调用方法的形式来间接调用函数,call和apply的第一个实参是要调用函数的母对象,它是调用上下文。

比如,以对象o的方法的形式调用函数f()并传入两个参数:f.call(o, 1, 2)

apply可以传入的参数可以是数组和类数组的。

6.4 bind()方法

ES5的该方法主要作用是将函数绑定至某个对象,方法返回一个新的函数,调用这个新的函数会把绑定的函数在对象中当做方法来调用。


function add(y) {
    return this.x + y;
}
var o = {
    x: 100
};
var g = add.bind(o);
console.log(g(100)); //200
  • 柯里化(currying):除了第一个实参外,传入bind的实参也会绑定至this;


function add(x, y) {
    return x + y;
}
var o = {

};
var g = add.bind(o, 100); //x是100
console.log(g(100)); //200 y是100

//上面的o对象可以去掉:
function add(x, y, z) {
    return x + y + z;
}
var g = add.bind(null, 100, 100, 100); //x是100 y是100 z是100
console.log(g()); //300

//又如:
function add(y, z) {
    return this.x + y + z;
}
var g = add.bind({x:100}, 100, 100); //{x是100} y是100 z是100
console.log(g()); //300

6.5 toString()方法

6.6 Function()构造函数

Function()构造函数所创建的函数并不是使用此法作用域:


var scope = "global";
function constructFunction () {
    var scope = "local";
    return new Function("return scope");
}
console.log(constructFunction()()); //global

var scope = "global";
function constructFunction () {
    var scope = "local";
    return function () {
        return scope
    };
}
console.log(constructFunction()()); //local

6.7 可调用的对象

7 函数式编程

7.1 使用函数处理数组

如要计算元素的平均值:

var arr = [1,2,3,4,5];
var total = 0;
for (var i = 0; i < arr.length; i++) {
    total += arr[i];
};
var result = total/arr.length;
console.log(result); //3

函数式编程风格:

var arr = [1,2,3,4,5];
function sum (a, b) {
    return a + b;
}
var total = arr.reduce(sum);
var result = total/arr.length;
console.log(result); //3

7.2 高阶函数

  • 高阶函数(higher-order function):就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数


function reverse(fun) {
    return function () {
        var result = fun.apply(this, arguments);
        return !result;
    }
}
var even = function (x) {
    return x % 2 === 0;
};

var odd = reverse(even);
console.log(odd(100)); //False
console.log(even(100)); //True

7.3 不完全函数

7.4 记忆

  • 记忆(memorization):部分结果在函数式编程中被缓存起来,这种缓存技巧就是“记忆”


JS菌
6.4k 声望2k 粉丝